#!/usr/local/python/bin/python
# coding=utf-8
__author__ = 'zht'

from model.FileColConfig import FileColConfig
from tdtools import LogUtil
from tools.ImpalaTypeUtil import ImpalaTypeUtil
from tdtools import Common_tdsql_prd as Common
from tdtools import Config_tdsql_prd as Config
from tdtools.FileUtil import FileUtil
from tools.Util import Util

shellCode = 'CHECK'
bizType = 'CHECK'

_fileInfoObj = Common._fileInfoObj
_logger=LogUtil._logger
_flgArrays = {}
_dataLines = 0
_fileColsInfoObj = Common._fileColsObj

def init():
    global _fileMetaInfo, _fileInfoObj, _appInfoObj, _dbInfoObj, _tDSQLCon, _bizdate, _dataFile, _tranFile, _hdfsDir, _hdfsFile, _retainDaysNum, _retainMonthsNum, _dbCode ,_fileColsInfoObj
    Common.checkArgs()

    Common.initFileInfo()
    _fileInfoObj = Common._fileInfoObj

    Common.initAppInfo()
    _appInfoObj = Common._appInfoObj

    Common.initDbInfo()
    _dbInfoObj = Common._dbInfoObj

    Common.initTdConnInfo()
    _tDSQLCon = Common._tDSQLCon

    Common.initFileCols()
    _fileColsInfoObj = Common._fileColsObj

    # 业务日期
    _bizdate = Common.getBizDate()
    # 载入文件路径
    _dataFile = Common.getSourceFilePath()

    # 元数据文件路径
    metaFile = Common.getMetaFilePath()

    _fileMetaInfo = Common.getFileMetaInfo(metaFile)

    # 数据库编码
    _dbCode = _dbInfoObj.getDbCode()

    # 需要保留的天数
    _retainDaysNum = _fileInfoObj.getKeepDayNum()

    # 保留月末的月数
    _retainMonthsNum = _fileInfoObj.getKeepMonthNum()


# 获取FLG文件中DAT文件大小
def getFlgSize():
    return int(_flgArrays[1])

# 检查文件大小
def checkFileSize(filename):
    try:
        # 获取DAT文件大小
        dataSize = FileUtil.fileSize(filename)
        flgSize = getFlgSize()
        message = 'flg文件中记录' + str(flgSize) + '字节，源文件实际' + str(dataSize) + '字节'
        if not dataSize == flgSize:
            _logger.error(Config.INFO_LEVEL, message + ' 失败')
            Common.exit(Config.RUNNING_ERROR, shellCode, message + ' 失败')
            Common.extExit(shellCode)
        else:
            _logger.info(Config.DEBUG_LEVEL, message + ' 成功')
    except Exception as e:
        _logger.exception(e,"检查文件大小异常")

# 获取flg内容
def getFlgFileContent():
    flgFile = Common.getMetaFilePath()
    global _flgArrays
    if FileUtil.fileExists(flgFile):
        # 获取flg文件的最后一行信息
        lastNum = FileUtil.getFileLineNums(flgFile)
        lineStr = FileUtil.getLineContent(flgFile, lastNum)
        _flgArrays = lineStr.split(' ')
        return len(_flgArrays) > 2
    else:
        return False

# 检查某个字段
def checkOneCol(colVal, colConfig):
    # 去空格
    colChgVal = colVal.strip()
    # 判断业务类型
    colType = colConfig.getColType().strip().upper()

    # 是否允许为空
    # message = '第' + str(colConfig.getColPos()) + '位字段是否允许为空'
    # if len(colChgVal) == 0 and not colConfig.isAllowNull():
    #     _logger.error(Config.INFO_LEVEL, message + ' 失败')
    #     Common.exit(Config.RUNNING_ERROR, shellCode, message + ' 失败')
    # else:
    #     _logger.info(Config.DEBUG_LEVEL, message + ' 成功')

    # 验证长度处理
    if not (colType in ['STRING', 'INT', 'FLOAT', 'DOUBLE', 'TINYINT', 'BIGINT', 'SMALLINT']
            and Util.isEmpty(colConfig.getColLength())):
        # print colChgVal+'----->'+str(len(colChgVal))+'----->'+colConfig.getColLength()
        if ',' in colConfig.getColLength():
            colArrays = colConfig.getColLength().split(',')
            message = '第' + str(
                colConfig.getColPos()) + '位字段，字段类型为' + colType + '，字段长度为' + colConfig.getColLength() + '，实际字段值为' + colChgVal
            if len(colArrays) == 2:
                intLen = int(colArrays[0])
                digitLen = int(colArrays[1])
            elif len(colArrays) == 1:
                intLen = int(colArrays[0])
                digitLen = 0
            else:
                _logger.error(Config.INFO_LEVEL, message)
                Common.exit(Config.RUNNING_ERROR, shellCode, message)
            if colType == "DECIMAL" and colChgVal.startswith("-"):
                intLen = intLen + 1
            if not ImpalaTypeUtil.checkDecimal(colChgVal, intLen, digitLen):
                _logger.error(Config.INFO_LEVEL, message)
                Common.exit(Config.RUNNING_ERROR, shellCode, message)
        else:
            # 判断长度
            message = '第' + str(colConfig.getColPos()) + '位字段长度最大为' + str(colConfig.getColLength()) \
                      + '，实际长度' + str(len(colChgVal))
            if len(colChgVal) > int(colConfig.getColLength()):
                _logger.error(Config.INFO_LEVEL, message + ' 失败')
                Common.exit(Config.RUNNING_ERROR, shellCode, message + ' 失败')
            else:
                _logger.info(Config.DEBUG_LEVEL, message + ' 成功')

    typeUtil = ImpalaTypeUtil()
    return typeUtil.checkType(colChgVal, colType)

# 检查任意一行的数据内容
def checkOneRecord(filename, lineNum):
    if getFlgSize() < 1073741824 or lineNum <= 10000:
        content = FileUtil.getLineContent(filename, lineNum)
        _logger.info('open read file ' + str(lineNum) + ' line ok!')
    else:
        content = FileUtil.getTheLineContent(filename, lineNum)
        _logger.info('linecache read file ' + str(lineNum) + ' line ok!')
    content = content.strip()

    # 判断任意一行数据是否为空
    message = '文件第' + str(lineNum) + '行内容不为空'
    if len(content) == 0:
        _logger.error(Config.INFO_LEVEL, message + ' 失败')
        Common.exit(Config.RUNNING_ERROR, shellCode, message + ' 失败')
    else:
        _logger.info(Config.DEBUG_LEVEL, message + ' 成功')

    # 判断配置检查的字段数量长度
    colSeprator = _fileInfoObj.getColSeprator()
    colConfs = _fileColsInfoObj.getData()
    vals = content.split(Util.decode(colSeprator))
    message = '文件第' + str(lineNum) + '行内容应该有' + str(len(colConfs)) \
              + '个字段，实际只有' + str(len(vals)) + '个字段'
    if len(vals) < len(colConfs):
        _logger.error(Config.INFO_LEVEL, message + ' 失败')
        Common.exit(Config.RUNNING_ERROR, shellCode, message + ' 失败')
    else:
        _logger.info(Config.DEBUG_LEVEL, message + ' 成功')

    # 循环判断字段长度
    for obj in colConfs:
        config = FileColConfig()
        config.setData(obj)

        # 检查字段的位置
        message = '字段' + str(config.getColName()) + '的位置为' \
                  + str(config.getColPos()) + '，总字段数是' + str(len(vals)) + '个'
        if config.getColPos() > len(vals):
            _logger.error(Config.INFO_LEVEL, message + ' 失败')
            Common.exit(Config.RUNNING_ERROR, shellCode, message + ' 失败')
        else:
            _logger.info(Config.DEBUG_LEVEL, message + ' 成功')

        if not _fileInfoObj.getFileSuffix().upper() == '.CSV':
            # 检查某个字段
            colVal = vals[config.getColPos() - 1]
            checkOneCol(colVal, config)

# 获取文件行数
def getFlgLineNums():
    global _dataLines
    _dataLines = int(_flgArrays[2])
    return _dataLines

# 检测文件内容
def checkRecord(filename):
    if getFlgLineNums() > 0:
        # 首尾加中间随机检查
        # 取第一行
        checkOneRecord(filename, 1)
        # 取随机一行
        if getFlgLineNums() > 2:
            checkOneRecord(filename, Util.getRandomNum(2, getFlgLineNums() - 1))
        # 最后一行
        if getFlgLineNums() > 1:
            checkOneRecord(filename, getFlgLineNums())
        FileUtil.clearcache()

def main():
    global _logFile, _logPrefix,_fileInfoObj
    # 初始化信息,TDSQL的连接信息、元数据文件路径、源文件路径、分割符、库表名称
    init()
    # 获取FLG内容
    getFlgFileContent()
    # 比较DAT与FLG大小
    checkFileSize(_dataFile)
    # 检查字段内容是否正确
    checkRecord(_dataFile)


if __name__ == "__main__":
    # todo 全局异常处理器
    # sys.excepthook = global_exception_handler
    try:
        main()
    except Exception as e:
        _logger.error(e, exc_info=True)
        Common.exit(Config.RUNNING_ERROR, shellCode, e)
    else:
        _logger.info("执行完成")
        Common.exit(Config.RUNNING_SUCCESS, shellCode,"脚本执行成功")